#include <QApplication>
#include <QLineEdit>
#include <QMetaObject>
#include <QMetaProperty>
#include <QWidgetAction>
#include <QMenu>
#include <QMouseEvent>
#include <QQueue>
#include <QTimer>
#include "ConfigData.h"
#include "config/ConfigItem.h"
#include "ConfigSearchBar.h"

ConfigSearchBarWidget::ConfigSearchBarWidget(ConfigDataModel *data, QWidget *parent) : QWidget(parent)
{
    m_data = data;
    m_searchBar = new QLineEdit(this);
    m_searchBar->setGeometry(0, 0, 120, 30);
    m_searchBar->installEventFilter(this);
    m_menu = new QMenu(this);
    m_menu->move(0, 30);
    m_menu->setWindowFlags(Qt::Widget);
    connect(m_searchBar, &QLineEdit::textChanged, this, &ConfigSearchBarWidget::onSearchBarContentChanged);
    resize(200, 200);

    QQueue<ConfigDataModel*> models;
    models.enqueue(data);

    while (!models.isEmpty())
    {
        ConfigDataModel *model = models.dequeue();
        const QMetaObject *obj = model->metaObject();
        for (int i = obj->propertyOffset(); i < obj->propertyCount(); i++)
        {
            QMetaProperty property = obj->property(i);
            if (property.read(model).canConvert<ConfigItem*>())
            {
                items.insert(property.read(model).value<ConfigItem*>());
            }
            else if (property.read(model).canConvert<ConfigDataModel*>())
            {
                models.enqueue(property.read(model).value<ConfigDataModel*>());
            }
        }
    }

}

bool ConfigSearchBarWidget::menuContainsText(const QString &text)
{
   for (auto action : m_menu->actions())
   {
       if (action->text() == text)
       {
           return true;
       }
   }
   return false;
}

void ConfigSearchBarWidget::removeUnusedActions(const QString& usedStr)
{
    for (auto action : m_menu->actions())
    {
        if (usedStr.isEmpty() ||
                    (!action->text().contains(usedStr, Qt::CaseInsensitive)  &&
                     !action->objectName().contains(usedStr, Qt::CaseInsensitive)))
        {
            m_menu->removeAction(action);
            action->deleteLater();
            if (action->menu())
            {
                action->menu()->deleteLater();
                for (auto actionInside : action->menu()->actions())
                {
                    actionInside->deleteLater();
                }
            }
        }
    }
}

bool ConfigSearchBarWidget::eventFilter(QObject *watched, QEvent *event)
{
    if (watched == m_searchBar && event->type() == QEvent::MouseButtonPress)
    {
        m_menu->show();
    }
    return QWidget::eventFilter(watched,  event);
}

void ConfigSearchBarWidget::onSearchBarContentChanged(const QString& str)
{
    m_menu->hide();
    removeUnusedActions(str);
    for (auto item : items)
    {
        if (!str.isEmpty() &&
                (item->objectName().contains(str, Qt::CaseInsensitive) ||
                 item->translatedName.contains(str, Qt::CaseInsensitive)) &&
                 !menuContainsText(item->translatedName))
        {
            QWidget *content = item->createRelatedWidget();
            if (content != nullptr)
            {
                QMenu *subMenu = new QMenu(item->translatedName, m_menu);
                subMenu->setObjectName(item->objectName());
                QWidgetAction *action = new QWidgetAction(nullptr);
                action->setDefaultWidget(content);
                subMenu->addAction(action);
                m_menu->addMenu(subMenu);
            }
        }
    }
    if (!m_menu->isEmpty())
        m_menu->show();
}

ConfigSearchBar::ConfigSearchBar(ConfigDataModel *data, QWidget *parent) : QLineEdit(parent)
{
    m_data = data;
    m_menu = new QMenu;
    m_menu->setWindowFlags(Qt::Widget);
    m_menu->setParent(parent);
    m_menu->setStyleSheet("QMenu{background:rgb(222, 222, 222);}");
    m_menu->hide();

    connect(this, &QLineEdit::textChanged, this, &ConfigSearchBar::onSearchBarContentChanged);
    resize(150, 50);

    QQueue<ConfigDataModel*> models;
    models.enqueue(data);

    while (!models.isEmpty())
    {
        ConfigDataModel *model = models.dequeue();
        const QMetaObject *obj = model->metaObject();
        for (int i = obj->propertyOffset(); i < obj->propertyCount(); i++)
        {
            QMetaProperty property = obj->property(i);
            if (property.read(model).canConvert<ConfigItem*>())
            {
                items.insert(property.read(model).value<ConfigItem*>());
            }
            else if (property.read(model).canConvert<ConfigDataModel*>())
            {
                models.enqueue(property.read(model).value<ConfigDataModel*>());
            }
        }
    }

    connect(qApp, &QApplication::focusChanged, this, [=](QWidget *old, QWidget *now){
            QWidget *source = now;
            while (source && source != parent && source != this && source != m_menu)
            {
                source = source->parentWidget();
            }
            if (source == parent)
            {
                m_menu->hide();
            }

        });
}

bool ConfigSearchBar::menuContainsText(const QString &text)
{
    for (auto action : m_menu->actions())
    {
        if (action->text() == text)
        {
            return true;
        }
    }
    return false;
}

void ConfigSearchBar::removeUnusedActions(const QString &usedStr)
{
    for (auto action : m_menu->actions())
    {
        if (usedStr.isEmpty() ||
                    (!action->text().contains(usedStr, Qt::CaseInsensitive)  &&
                     !action->objectName().contains(usedStr, Qt::CaseInsensitive)))
        {
            m_menu->removeAction(action);
            action->deleteLater();
            if (action->menu())
            {
                action->menu()->deleteLater();
                for (auto actionInside : action->menu()->actions())
                {
                    actionInside->deleteLater();
                }
            }
        }
    }
}

void ConfigSearchBar::focusInEvent(QFocusEvent *e)
{
    QLineEdit::focusInEvent(e);
    if (!m_menu->isEmpty())
    {
        m_menu->popup(this->mapToParent(this->rect().bottomLeft()));
        m_menu->raise();
    }
}




void ConfigSearchBar::focusOutEvent(QFocusEvent *e)
{
    QLineEdit::focusOutEvent(e);

    // m_menu->hide();
}


void ConfigSearchBar::onSearchBarContentChanged(const QString &str)
{
    m_menu->hide();
    removeUnusedActions(str);
    for (auto item : items)
    {
        if (!str.isEmpty() &&
                (item->objectName().contains(str, Qt::CaseInsensitive) ||
                 item->translatedName.contains(str, Qt::CaseInsensitive)) &&
                 !menuContainsText(item->translatedName))
        {
            QWidget *content = item->createRelatedWidget();
            if (content != nullptr)
            {
                QMenu *subMenu = new QMenu(item->translatedName, m_menu);
                subMenu->setObjectName(item->objectName());
                QWidgetAction *action = new QWidgetAction(nullptr);
                action->setDefaultWidget(content);
                subMenu->addAction(action);
                m_menu->addMenu(subMenu);
            }
        }
    }
    if (!m_menu->isEmpty())
    {
        m_menu->popup(this->mapToParent(this->rect().bottomLeft()));
        m_menu->raise();
    }
}
